//
// Created by LvenLiu on 2022/3/25.
//


#include "FFPlayer.h"

// 音视频上下文
AVFormatContext *formatContext = nullptr;
// 视频解码
VideoChannel *videoChannel = nullptr;

/**
 * 设置视频路径
 */
void FFPlayer::setPath(const char *path) {
    // 由于C层结尾有\0 所以长度加1
    this->path = new char[strlen(path) + 1];
    strcpy(this->path, path);
}

/**
 * jni 回调 java层
 */
void FFPlayer::setCallback(JNICallback *callback) {
    this->callback = callback;
}

FFPlayer::FFPlayer() {
}

// 回收
FFPlayer::~FFPlayer() {
    LOGE("~FFPlayer");
    isStar = false;
    if (path) {
        delete path;
        path = nullptr;
    }
    if (callback) {
        delete callback;
        callback = nullptr;
    }
}

void FFPlayer::onPrepared(int thread_mode) {
    if (callback) {
        callback->onPrepared(thread_mode);
    }
}

void FFPlayer::onError(int thread_mode, int error_code, char *msg) {
    if (callback) {
        callback->onError(thread_mode, error_code, msg);
    }
    // 回收
    if (formatContext) {
        avformat_free_context(formatContext);
        formatContext = nullptr;
    }
}

// prepare 在子线程执行准备任务
void *task_prepare(void *args) {
    FFPlayer *player = reinterpret_cast< FFPlayer *>(args);
    player->prepare_();
    return nullptr;
}

/**
 * 这里是真正准备的方法
 * 1. 开辟音视频 上下文
 * 2. 查找视频信息
 * 3. 拿到音视频解码器，为start方法解码做准备
 */
void FFPlayer::prepare_() {
    // 1. 视频格式，上下文
    formatContext = avformat_alloc_context();
    // 字典，配置一些信息
    AVDictionary *dictionary = 0;
    // 1秒 = 1000毫秒   1 毫秒 = 1000微秒
    av_dict_set(&dictionary, "timeout", "5000000", 0); // 单位微妙
    /**
     * 1. 打开视频格式
     * 1，AVFormatContext *
     * 2，const char *url 路径
     * 3，AVInputFormat *fmt  Mac、Windows 摄像头、麦克风， 目前安卓用不到
     * 4，AVDictionary **options 各种设置：例如：Http 连接超时， 打开rtmp的超时
     */
    int r = avformat_open_input(&formatContext, this->path, 0, &dictionary); // 0 on success

    // 释放字典
    av_dict_free(&dictionary);

    if (r) {
        // TODO 回调到Java层  av_err2str(r) 将code转成string
        onError(THREAD_CHILD, AV_FORMAT_OPEN_INPUT, av_err2str(r));
        return;
    }
    // 2. 查找音视频流信息
    r = avformat_find_stream_info(formatContext, 0); // >=0 if OK
    if (r < 0) {
        // TODO 回调到Java层
        onError(THREAD_CHILD, AV_FORMAT_FIND_STREAM_INFO, av_err2str(r));
        return;
    }
    bool succeed = false;
    // 根据流的个数，查找对应的解码器（音频 视频 字幕等等）
    for (int stream_index = 0; stream_index < formatContext->nb_streams; ++stream_index) {
        // 得到媒体流
        AVStream *stream = formatContext->streams[stream_index];
        // 从媒体流得到编码 解码的参数
        AVCodecParameters *parameters = stream->codecpar;
        // 3. 根据参数，获取音视频解码器
        AVCodec *codec = avcodec_find_decoder(parameters->codec_id);
        if (!codec) {
            // TODO 回调到Java层
            onError(THREAD_CHILD, AV_CODEC_FIND_DECODER, av_err2str(r));
            continue;
        }
        // 获取编解码器上下文，真正处理数据
        AVCodecContext *codecContext = avcodec_alloc_context3(codec);
        if (!codecContext) {
            // TODO 回调到Java层
            onError(THREAD_CHILD, AV_CODEC_ALLOC_CONTEXT, av_err2str(r));
            continue;
        }
        // 4. 将解码的参数赋值给解码器
        r = avcodec_parameters_to_context(codecContext, parameters);// >= 0 on success
        if (r < 0) {
            // TODO 回调到Java层
            onError(THREAD_CHILD, AV_CODEC_PARAMETERS_TO_CONTEXT, av_err2str(r));
            continue;
        }
        // 5. 打开解码器
        r = avcodec_open2(codecContext, codec, 0); // 0 on success
        if (r) {
            // TODO 回调到Java层
            onError(THREAD_CHILD, AV_CODEC_OPEN, av_err2str(r));
            continue;
        }
        AVMediaType type = parameters->codec_type;
        // 根据媒体流的数据，再具体处理
        if (type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
            // 音频
            succeed = true;
        } else if (type == AVMediaType::AVMEDIA_TYPE_VIDEO) {
            // 视频
            succeed = true;
            videoChannel = new VideoChannel(stream_index, codecContext);
            videoChannel->setRenderCallback(renderCallback);
        }
    } // for end

    if (!succeed) {
        // TODO 回调到Java层
        //char *msg = "没有音视频数据";
        onError(THREAD_CHILD, AV_FORMAT_NULL, av_err2str(r));
        return;
    }

    // 准备成功 回调
    onPrepared(THREAD_CHILD);
}

void FFPlayer::prepare() {
    // 开线程去做准备工作
    pthread_create(&prepare_pid, nullptr, task_prepare, this);
}

void *start_task(void *args) {
    FFPlayer *player = reinterpret_cast< FFPlayer *>(args);
    player->start_();
    return 0;
}


/**
 * 解码第一步
 * 开始读取 音视频压缩包 并添加到队列
 */
void FFPlayer::start_() {
    LOGE("start_ start ... ");
    while (this->isStar) {
        // 音视频压缩包
        AVPacket *packet = av_packet_alloc();
        int r = av_read_frame(formatContext, packet);// 0 if OK, < 0 on error or end of file
        if (r == 0) {
            if (videoChannel && videoChannel->streamIndex() == packet->stream_index) {
                // 代表是视频
                videoChannel->addAVPacket(packet);
            }
        } else if (r == AVERROR_EOF) {
            // TODO 文件结尾
        } else {
            break;
        }
    }
    this->isStar = false;
    if (videoChannel) {
        videoChannel->stop();
        delete videoChannel;
        videoChannel = nullptr;
    }
    // 回收
    if (formatContext) {
        avformat_free_context(formatContext);
        formatContext = nullptr;
    }

    LOGE("start_ end ... ");
}

void FFPlayer::start() {
    this->isStar = true;
    if (videoChannel) {
        videoChannel->start();
    }
    // 1. 开始解码播放
    pthread_create(&start_pid, nullptr, start_task, this);
}

void FFPlayer::setRenderCallback(RenderCallback renderCallback) {
    this->renderCallback = renderCallback;
}









